﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.Serialization;

namespace Soundelle.Oscillator
{
    public class BaseDevice
    {
        public BaseDevice()
        {
            _GUID = System.Guid.NewGuid();
            _TimePosition = 0;
            Function = AvailableFunctions[0];
            InitRmsRatio();
        }


        public enum DeviceFunctionType
        {
            Function_Generator,
            Function_GeneratorSine,
            Function_GeneratorSquare,
            Function_GeneratorTriangle,
            Function_GeneratorRandom,

            Function_Filter,
            Function_FilterLPF,
            Function_FilterHPF,
            Function_FilterAverage,
            Function_FilterAverageMS,
            Function_FilterAverageRMS,

            Function_Calculator,
            Function_CalculatorIntegration,
            Function_CalculatorDerivation,
            Function_CalculatorTachometer,

            Function_Player,
            Function_FileWriter,

            Function_Thru
        }

        public enum ParameterType
        {
            Parameter_DC_Offset,
            Parameter_Amplitude,
            Parameter_Frequency,
            Parameter_Phase,
            Parameter_Duty,
            Parameter_IsZeroPeak,
            Parameter_Distribution,
            Parameter_Averages,
            Parameter_Threshold,
            Parameter_Count
        }

        public enum DeviceRoleType
        {
            Role_Mixer_MixingSignal,    // thru out without gain
            Role_Mixer_InputSignal,     // thru out with gain
            Role_Mixer_DisableSignal,   // go to trash box

            Role_Amplitude,
            Role_Frequency,
            Role_Phase,
            Role_Duty,

            Role_Modulator_Amplitude,
            Role_Modulator_Frequency,
            Role_Modulator_Phase,
            Role_Modulator_Duty,

            Role_Multiplier_Amplitude,
            Role_Multiplier_Frequency,
            Role_Multiplier_Phase,
            Role_Multiplier_Duty

        }

        #region "Parameters"

        private ParameterType[] _AvailableParameters = {
            ParameterType.Parameter_DC_Offset,
            ParameterType.Parameter_Amplitude
            //ParameterType.Parameter_Frequency,
            //ParameterType.Parameter_Phase,
            //ParameterType.Parameter_Duty,
            //ParameterType.Parameter_IsZeroPeak,
            //ParameterType.Parameter_Distribution,
            //ParameterType.Parameter_Averages,
            //ParameterType.Parameter_Threshold,
            //ParameterType.Parameter_Count
        };
        virtual public ParameterType[] AvailableParameters
        {
            get
            { return _AvailableParameters; }
        }

        private double _Parameter_DC_Offset = 0.0;
        public double Parameter_DC_Offset
        {
            get
            { return _Parameter_DC_Offset; }
            set
            { _Parameter_DC_Offset = value; }
        }

        private double _Parameter_Amplitude = 1.0;
        public double Parameter_Amplitude
        {
            get
            { return _Parameter_Amplitude; }
            set
            { _Parameter_Amplitude = value; }
        }

        public double Parameter_AmplitudeRms
        {
            get
            { return Parameter_Amplitude * RmsRatio; }
            set
            { Parameter_Amplitude = value / RmsRatio; }
        }

        private double _Parameter_Frequency = 1000.0;
        public double Parameter_Frequency
        {
            get
            { return _Parameter_Frequency; }
            set
            { _Parameter_Frequency = value; }
        }

        private double _Parameter_Phase = 0.0;
        public double Parameter_Phase
        {
            get
            { return _Parameter_Phase; }
            set
            { _Parameter_Phase = value; }
        }

        private double _Parameter_Duty = 0.5;
        public double Parameter_Duty
        {
            get
            { return _Parameter_Duty; }
            set
            {
                if (0.0 <= value && value <= 1.0)
                {
                    _Parameter_Duty = value;
                    InitRmsRatio();
                }
            }
        }

        private bool _Parameter_IsZeroPeak = false;
        public bool Parameter_IsZeroPeak
        {
            get
            { return _Parameter_IsZeroPeak; }
            set
            {
                _Parameter_IsZeroPeak = value;
                InitRmsRatio();
            }
        }

        private double _Parameter_Distribution = 1.0;
        public double Parameter_Distribution
        {
            get
            { return _Parameter_Distribution; }
            set
            { _Parameter_Distribution = value; }
        }

        private long _Parameter_Averages = 1;
        public long Parameter_Averages
        {
            get
            { return _Parameter_Averages; }
            set
            {
                if (value >0)
                    _Parameter_Averages = value;
            }
        }

        private double _Parameter_Threshold = 0.5;
        public double Parameter_Threshold
        {
            get
            { return _Parameter_Threshold; }
            set
            { _Parameter_Threshold = value; }
        }

        private double _Parameter_Count = 1;
        public double Parameter_Count
        {
            get
            { return _Parameter_Count; }
            set
            { _Parameter_Count = value; }
        }

        private double[] _Parameter_Weighting = new double[0];
        public double[] Parameter_Weighting
        {
            get
            { return _Parameter_Weighting; }
            set
            { _Parameter_Weighting = value; }
        }
        #endregion

        #region "Properties"

        private bool _Enabled = true;
        public bool Enabled
        {
            get
            { return _Enabled; }
            set
            { _Enabled = value; }
        }

        private DeviceFunctionType[] _AvailableFunctions = new DeviceFunctionType[] {
             DeviceFunctionType.Function_Thru
        };
        virtual public DeviceFunctionType[] AvailableFunctions
        {
            get
            { return _AvailableFunctions; }
        }
        private DeviceFunctionType _Function = DeviceFunctionType.Function_Thru;
        public DeviceFunctionType Function
        {
            get
            { return _Function; }
            set
            {
                for (int i = 0; i < AvailableFunctions.Length; ++i)
                {
                    if (AvailableFunctions[i] == value)
                    {
                        _Function = value;
                        InitFunction();
                        InitRmsRatio();
                        break;
                    }
                }
            }
        }

        private DeviceRoleType _Role = DeviceRoleType.Role_Mixer_MixingSignal;
        public DeviceRoleType Role
        {
            get
            { return _Role; }
            set
            { _Role = value; }
        }

        private List<BaseDevice> _Devices = new List<BaseDevice>();
        public List<BaseDevice> Devices
        {
            get
            { return _Devices; }
        }

        private double _SamplingRate = 48000.0;
        public double SamplingRate
        {
            get
            { return _SamplingRate; }
            set
            {
                if (value > 0)
                {
                    _SamplingRate = value;
                    if (this.Devices != null)
                    {
                        foreach (BaseDevice dev in this.Devices)
                        {
                            dev.SamplingRate = value;
                        }
                    }
                }
            }
        }

        private double _TimePosition = 0.0;
        protected double TimePosition
        {
            get
            { return _TimePosition; }
            set
            { _TimePosition = value; }
        }


        private double _RmsRatio = 1.0;
        protected double RmsRatio
        {
            get
            { return _RmsRatio; }
            set
            { _RmsRatio = value; }
        }

        private System.Guid _GUID;
        public System.Guid GUID
        {
            get
            { return _GUID; }
        }

        private double _PreviousSignalOut = 0.0;
        public double PreviousSignalOut
        {
            get
            { return _PreviousSignalOut; }
        }
        protected void SetPreviousSignalOut(double Value)
        {
            _PreviousSignalOut=Value;
        }

        #endregion

        public override string ToString()
        {
            return base.ToString() + " " +  GUID.ToString();
        }

        protected virtual void InitRmsRatio()
        {
        }

        protected virtual void InitFunction()
        {
        }

        public void Add(ref BaseDevice NewDevice)
        {
            if (NewDevice != null)
            {
                NewDevice.SamplingRate = this.SamplingRate;
                NewDevice.Reset();

                _Devices.Add(NewDevice);
            }
        }

        public void Clear()
        {
            _Devices.Clear();
        }

        public void GetDeviceDictionary(ref Dictionary<System.Guid, BaseDevice> DeviceDictionary)
        {
            DeviceDictionary.Add(this.GUID, this);

            foreach (BaseDevice dev in this.Devices)
            {
                dev.GetDeviceDictionary(ref DeviceDictionary);
            }
        }

        public virtual void Reset()
        {
            TimePosition = 0.0;
            SetPreviousSignalOut(0.0);

            InitRmsRatio();
            InitFunction();

            foreach (BaseDevice dev in this.Devices)
            {
                dev.Reset();
            }
        }

        public virtual double SignalOut()
        {
            if (!Enabled)
                return 0.0;

            double signalOut = 0.0;
            double signalIn = 0.0;

            double DcOffset = this.Parameter_DC_Offset;
            double amplitude = this.Parameter_Amplitude;
            double frequency = 0.0;
            double phase = 0.0;
            double duty = 0.0;

            DoModulationByChildDevices(ref signalOut, ref signalIn, ref amplitude, ref frequency, ref phase, ref duty);

            ++TimePosition;
            double SignalOut= amplitude * signalIn + signalOut + DcOffset;
            SetPreviousSignalOut(SignalOut);
            return SignalOut;
        }

        protected void DoModulationByChildDevices(ref double signalOut, ref double signalIn, ref double amplitude, ref double frequency, ref double phase, ref double duty)
        {
            double trash = 0.0;

            //--- Sweep
            foreach (BaseDevice dev in this.Devices)
            {
                if (dev.Enabled)
                {
                    switch (dev.Role)
                    {
                        case DeviceRoleType.Role_Amplitude:
                            amplitude = dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Frequency:
                            frequency = dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Phase:
                            phase = dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Duty:
                            duty = dev.SignalOut();
                            break;

                        default:
                            break;
                    }
                }
            }

            //--- Except Sweep
            foreach (BaseDevice dev in this.Devices)
            {
                if (dev.Enabled)
                {
                    switch (dev.Role)
                    {
                        case DeviceRoleType.Role_Mixer_MixingSignal:
                            signalOut += dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Mixer_InputSignal:
                            signalIn += dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Modulator_Amplitude:
                            amplitude += dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Modulator_Frequency:
                            frequency += dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Modulator_Phase:
                            phase += dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Modulator_Duty:
                            duty += dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Multiplier_Amplitude:
                            amplitude *= dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Multiplier_Frequency:
                            frequency *= dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Multiplier_Phase:
                            phase *= dev.SignalOut();
                            break;
                        case DeviceRoleType.Role_Multiplier_Duty:
                            duty *= dev.SignalOut();
                            break;

                        case DeviceRoleType.Role_Amplitude:
                        case DeviceRoleType.Role_Frequency:
                        case DeviceRoleType.Role_Phase:
                        case DeviceRoleType.Role_Duty:
                            break;

                        default:
                            trash = dev.SignalOut();
                            break;
                    }
                }
            }
        }

        public virtual void ParseFromXml(string XmlText, bool WithGuid)
        {
        }

        public virtual string ParseToXml()
        {
            string XmlText = "";

            XmlText += "<OscilatorDevice>\n";
            XmlText += ParseParam("Parameter", "guid", GUID.ToString());
            XmlText += "</OscilatorDevice>\n";

            return XmlText;

        }

        private string ParseParam(string node, string name, string value)
        {
            return "\t<" + node + " name=\"" + name + "\" value=\"" + value + "\"/>\n";
        }

    }
}
